package com.onec.service.gateway.config;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.json.JSONUtil;
import com.onec.service.api.content.AuthConstants;
import com.onec.service.api.dto.response.ResponseData;
import com.onec.service.api.enums.ResultCodeEnum;
import com.onec.service.gateway.filter.IgnoreUrlsRemoveJwtFilter;
import com.onec.service.gateway.security.AuthorizationManager;
import java.nio.charset.StandardCharsets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import reactor.core.publisher.Mono;

/**
 * 资源服务器配置
 *
 * @author onec
 */
@Configuration
@EnableWebFluxSecurity
public class ResourceServerConfig {

  @Autowired
  private AuthorizationManager authorizationManager;
  @Autowired
  private WhiteListConfig whiteListConfig;
  @Autowired
  private IgnoreUrlsRemoveJwtFilter ignoreUrlsRemoveJwtFilter;

  @Bean
  public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    http.oauth2ResourceServer().jwt()
        .jwtAuthenticationConverter(jwtAuthenticationConverter());
    //自定义处理JWT请求头过期或签名错误的结果
    http.oauth2ResourceServer().authenticationEntryPoint(authenticationEntryPoint());
    //对白名单路径，直接移除JWT请求头
    http.addFilterBefore(ignoreUrlsRemoveJwtFilter, SecurityWebFiltersOrder.AUTHENTICATION);

    http.authorizeExchange()
        .pathMatchers(ArrayUtil.toArray(whiteListConfig.getUrls(), String.class)).permitAll()
        .anyExchange().access(authorizationManager)
        .and()
        .exceptionHandling()
        // 处理未授权
        .accessDeniedHandler(accessDeniedHandler())
        //处理未认证
        .authenticationEntryPoint(authenticationEntryPoint())
        .and().csrf().disable();

    return http.build();
  }


  /**
   * 未授权
   */
  @Bean
  ServerAccessDeniedHandler accessDeniedHandler() {
    return (exchange, denied) -> {
      Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
          .flatMap(response -> {
            ResponseData responseData = ResponseData.error(ResultCodeEnum.USER_ACCESS_UNAUTHORIZED);
            DataBuffer buffer = response.bufferFactory()
                .wrap(JSONUtil.toJsonStr(responseData).getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(buffer))
                .doOnError(error -> DataBufferUtils.release(buffer));
          });

      return mono;
    };

  }

  /**
   * token无效或者已过期自定义响应
   */
  @Bean
  ServerAuthenticationEntryPoint authenticationEntryPoint() {
    return (exchange, e) -> {
      Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
          .flatMap(response -> {
            response.setStatusCode(HttpStatus.OK);
            ResponseData responseData = ResponseData.error(ResultCodeEnum.USER_ACCESS_LOCK);
            DataBuffer buffer = response.bufferFactory()
                .wrap(JSONUtil.toJsonStr(responseData).getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(buffer))
                .doOnError(error -> DataBufferUtils.release(buffer));
          });
      return mono;
    };
  }


  /**
   * @link https://blog.csdn.net/qq_24230139/article/details/105091273
   * ServerHttpSecurity没有将jwt中authorities的负载部分当做Authentication 需要把jwt的Claim中的authorities加入
   * 方案：重新定义ReactiveAuthenticationManager权限管理器，默认转换器JwtGrantedAuthoritiesConverter
   */
  @Bean
  public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
    JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
    jwtGrantedAuthoritiesConverter.setAuthorityPrefix(AuthConstants.AUTHORITY_PREFIX);
    jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(AuthConstants.AUTHORITY_CLAIM_NAME);

    JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
    return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
  }
}
